/* tools/mkbootimg/exbootimg.c
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>

#include "bootimg.h"

#define ALIGN(x, a)		((x + (a - 1)) & ~(a - 1))

static void *load_file(const char *fn, unsigned *_sz)
{
    char *data;
    int fd;
	int rsz;

    data = 0;
    fd = open(fn, O_RDONLY);
    if(fd < 0) return 0;

	rsz = lseek(fd, 0, SEEK_END);
	if(rsz < 0) goto oops;

	if(lseek(fd, 0, SEEK_SET) != 0) goto oops;

	data = (char*) malloc(rsz);
	if(data == 0) goto oops;

	if(read(fd, data, rsz) != rsz) goto oops;

	*_sz = rsz;
	close(fd);
    return data;
oops:
    close(fd);
    if(data != 0) free(data);
    return 0;
}

static int save_file(const char *fn, void *data, int len)
{
	int fd;
	fd = open(fn, O_WRONLY | O_TRUNC | O_CREAT, 0664);
	if(fd < 0) return 1;

	if(write(fd, data, len) != len) {
		close(fd);
		unlink(fn);
		return 1;
	}
	close(fd);
	return 0;
}

static void print_id(const uint8_t *id, size_t id_len)
{
	unsigned int i;
    printf("0x");
    for (i = 0; i < id_len; i++) {
        printf("%02x", id[i]);
    }
    printf("\n");
}

static int dump_bootimg(const char *filename)
{
	int fd;
	int sz = sizeof(boot_img_hdr);
	boot_img_hdr hdr;

	fd = open(filename, O_RDONLY);
    if(fd < 0) return 1;

	if(read(fd, &hdr, sz) != sz) goto oops;

	printf("dump '%s':\n", filename);
	printf("pagesize @ (%d)\n", hdr.page_size);
	printf("kernel   @ %08x (%d)\n", hdr.kernel_addr, hdr.kernel_size);
	printf("ramdisk  @ %08x (%d)\n", hdr.ramdisk_addr, hdr.ramdisk_size);
	printf("second   @ %08x (%d)\n", hdr.second_addr, hdr.second_size);
	printf("module   @ %08x (%d)\n", hdr.unused[1], hdr.unused[0]);
	printf("tags     @ %08x\n", hdr.tags_addr);
	printf("SHA id   @ "); print_id((uint8_t *) hdr.id, sizeof(hdr.id));
	printf("name     @ %s\n", hdr.name);
	printf("cmdline  @ %s\n", hdr.cmdline);
	printf("excmd    @ %s\n", hdr.extra_cmdline);

	close(fd);
	return 0;
oops:
	close(fd);
	return 1;
}

int usage(void)
{
    fprintf(stderr,"get module stage file from boot image.\n"
			"usage: exbootimg\n"
            "       -b|--bootimg <filename>\n"
            "       [ -l|--list]   dump the input image\n"
            "       [ -a|--all]    extract all image\n"
			"       [ -k|--kernel <filename>\n"
            "       [ -r|--ramdisk <filename> ]\n"
            "       [ -s|--second <2ndbootloader-filename> ]\n"
            "       [ -m|--module <kernel-module-filename> ]\n"
            );
    return 1;
}

int main(int argc, char **argv)
{
    boot_img_hdr* phdr;
	void *raddr;
	int sz;

    char *bootimg_fn = NULL;
    void *bootimg_data = NULL;
    char *kernel_fn = NULL;
    char *ramdisk_fn = NULL;
    char *second_fn = NULL;
    char *module_fn = NULL;
    int fd;
	bool dump = 0;
	bool all = 0;

    argc--;
    argv++;

    while(argc > 0){
		if(!strcmp(argv[0], "--list") || !strcmp(argv[0], "-l")) {
			dump = 1;
			argv++;
			argc--;
			continue;
		} if(!strcmp(argv[0], "--all") || !strcmp(argv[0], "-a")) {
			all = 1;
			argv++;
			argc--;
			continue;
		}

        if(argc >= 2) {
			char *arg = argv[0];
            char *val = argv[1];
            argc -= 2;
            argv += 2;
            if(!strcmp(arg, "--bootimg") || !strcmp(arg, "-b")) {
                bootimg_fn = val;
            } else if(!strcmp(arg, "--kernel") || !strcmp(arg, "-k")) {
                kernel_fn = val;
            } else if(!strcmp(arg, "--ramdisk") || !strcmp(arg, "-r")) {
                ramdisk_fn = val;
            } else if(!strcmp(arg, "--second") || !strcmp(arg, "-s")) {
                second_fn = val;
            } else if(!strcmp(arg, "--module") || !strcmp(arg, "-m")) {
                module_fn = val;
            } else {
                return usage();
            }
        } else {
            return usage();
        }
    }

	if(bootimg_fn == 0) {
        fprintf(stderr,"error: no input filename specified\n");
        return usage();
    }

	if(dump) {
		return dump_bootimg(bootimg_fn);
	}

	if(all) {
		if(!kernel_fn) kernel_fn = "zImage";
		if(!ramdisk_fn) ramdisk_fn = "ramdisk.img";
		if(!second_fn) second_fn = "second.img";
		if(!module_fn) module_fn = "modules.tar.bz2";
	}

	bootimg_data = load_file(bootimg_fn, &sz);
	if(!bootimg_data || sz <= sizeof(boot_img_hdr)) {
		fprintf(stderr, "error: read boot image header.\n");
		return 1;
	}

	raddr = bootimg_data;
	phdr = (boot_img_hdr *)bootimg_data;

	if(!memcpy(phdr, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
		fprintf(stderr, "error: bad magic.\n");
		return 1;
	}

	raddr += phdr->page_size;	// head(padding) size
	if(kernel_fn && phdr->kernel_size) {
		if(save_file(kernel_fn, raddr, phdr->kernel_size))
			fprintf(stderr, "error: failed to extract kernel.\n");
		else
			printf("extract kernel file : '%s' OK\n", kernel_fn);
	}

	raddr += ALIGN(phdr->kernel_size, phdr->page_size);
	if(ramdisk_fn && phdr->ramdisk_size) {
		if(save_file(ramdisk_fn, raddr, phdr->ramdisk_size))
			fprintf(stderr, "error: failed to extract ramdisk.\n");
		else
			printf("extract ramdisk file : '%s' OK\n", ramdisk_fn);
	}

	raddr += ALIGN(phdr->ramdisk_size, phdr->page_size);
	if(second_fn && phdr->second_size) {
		if(save_file(second_fn, raddr, phdr->second_size))
			fprintf(stderr, "error: failed to extract second.\n");
		else
			printf("extract second file : '%s' OK\n", second_fn);
	}

	raddr += ALIGN(phdr->second_size, phdr->page_size);
	if(module_fn && phdr->unused[0]) {
		if(save_file(module_fn, raddr, phdr->unused[0]))
			fprintf(stderr, "error: failed to extract module.\n");
		else
			printf("extract module file : '%s' OK\n", module_fn);
	}

	free(bootimg_data);
    return 0;
}
